#region Terms of Usage
/*
 * Copyright 2006 Sameera Perera
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using Db4objects.Db4o;

using needle.Properties;

using System.Collections;

namespace needle.Services.soab.Data
{
    public class Database
    {
        private IObjectContainer db;
        private ReaderWriterLock dbLock;

        public static readonly Database Instance = new Database();

        protected Database()
        {
            dbLock  = new ReaderWriterLock();
        }

        public void AcquireReaderLock() { dbLock.AcquireReaderLock( -1 ); }
        public void AcquireWriterLock() { dbLock.AcquireWriterLock( -1 ); }
        public void ReleaseReaderLock() { dbLock.ReleaseReaderLock(); }
        public void ReleaseWriterLock() { dbLock.ReleaseWriterLock(); }

        public IObjectContainer RawDatabase { get { return this.db; } }

        /// <summary>Gets the object matching the given prototype.</summary>
        /// <param name="prototype">The template to which objects are matched.</param>
        /// <returns>The object matching the given template. If multiple matches are found,
        /// only the first match is returned.</returns>
        public object GetObject( object prototype )
        {
            object retVal;
            dbLock.AcquireReaderLock( -1 );
            IObjectSet os  = db.Get( prototype );
            dbLock.ReleaseReaderLock();

            if ( os.Count > 0 )
                retVal = os[ 0 ];
            else
                retVal = null;

            return retVal;
        }

        public void RefreshObject( object liveObject, int refreshDepth )
        {
            dbLock.AcquireReaderLock( -1 );
            db.Ext().Refresh( liveObject, refreshDepth );
            dbLock.ReleaseReaderLock();
        }

        /// <summary>
        /// Gets an iterator for all objects found based on the given template.
        /// </summary>
        /// <param name="prototype">The template to which objects are matched.</param>
        /// <returns>An iterator for objects matching the given template.</returns>
        public IEnumerable FindObjects( object prototype )
        {
            dbLock.AcquireReaderLock( -1 );
            IObjectSet os    = db.Get( prototype );
            while ( os.HasNext() )
                yield return os.Next();
            dbLock.ReleaseReaderLock();
        }

        /// <summary>
        /// Gets an array containing all objects of the given type matching the given
        /// object template.
        /// </summary>
        /// <typeparam name="T">The type of objects to be found.</typeparam>
        /// <param name="prototype">The object template to be matched.</param>
        /// <returns>An array containing objects of the given type, matching the template.</returns>
        public T[] FindObjects<T>( T prototype )
        {
            dbLock.AcquireReaderLock( -1 );
            
            IObjectSet os    = db.Get( prototype );
            T[] retVal      = new T[ os.Count ];
            for ( int i = 0; i < retVal.Length; i++ )
                retVal[ i ] = (T)os[ i ];
            
            dbLock.ReleaseReaderLock();
            return retVal;
        }

        /// <summary>
        /// Gets an array containing all objects of the given type stored in the database.
        /// </summary>
        /// <typeparam name="T">The type of objects to be found.</typeparam>
        /// <returns>An array containing objects of the given type.</returns>
        public T[] FindObjects<T>()
        {
            dbLock.AcquireReaderLock( -1 );
            
            IObjectSet os    = db.Get( typeof(T) );
            T[] retVal      = new T[ os.Count ];
            for ( int i = 0; i < retVal.Length; i++ )
                retVal[ i ] = (T)os[ i ];
            
            dbLock.ReleaseReaderLock();
            return retVal;
        }

        ///// <summary>Returns the unique ID by which a specified object is identified within the 
        ///// offline database.</summary>
        ///// <param name="item">The object whose ID's required.</param>
        ///// <returns>The ID of the specified object.</returns>
        //public long GetObjectUID( object item )
        //{
        //    return db.Ext().GetID( item );
        //}

        ///// <summary>Returns the object with the given unique ID.</summary>
        ///// <param name="uid">The ID of the object required.</param>
        ///// <returns>The object with the specified ID.</returns>
        //public object GetObjectByUID( long uid )
        //{
        //    return db.Ext().GetByID( uid );
        //}

        /// <summary>Saves the given object to the database</summary>
        /// <param name="data">The data to be saved.</param>
        public void Save( object data )
        {
            dbLock.AcquireWriterLock( -1 );
            db.Set( data );
            dbLock.ReleaseWriterLock();
        }

        /// <summary>Deletes the given object from the database.</summary>
        /// <param name="data">The object to be deleted from the database.</param>
        public void Delete( object data )
        {
            dbLock.AcquireWriterLock( -1 );
            db.Delete( data );
            dbLock.ReleaseWriterLock();
        }

        /// <summary>
        /// Closes the offline databse.
        /// </summary>
        public void Close()
        {
            dbLock.AcquireWriterLock( -1 );
            db.Close();
            dbLock.ReleaseWriterLock();
        }

        /// <summary>
        /// Opens the offline database.
        /// </summary>
        public void Open()
        {
            dbLock.AcquireWriterLock( -1 );
            db  = Db4oFactory.OpenFile(Settings.Default.CacheDatabasePath );
            dbLock.ReleaseWriterLock();
        }


    }
}
